#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/epoll.h>
#include <libgen.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "sqlite3.h"
#include "sqlaction.h"
#include "logger.h"
#include "socketinit.h"
#include <signal.h>

#define MAX_EVENTS 1024
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/*---------------------------全局变量------------------------------*/
info_temp_t					*divi_info;
struct epoll_event			epoll;
struct epoll_event			epoll_array[MAX_EVENTS];
int							sockfd= -1;
int							p_stop=0;
sqlite3						*db;
struct sockaddr_in			cliaddr;
struct sockaddr_in			servaddr;
socklen_t					len=sizeof(cliaddr);
int							epollfd=0;
int							clifd = -1;

/*---------------------------函数声明------------------------------*/
void useage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);
	printf(" %s is a socket server program, which used to verify client and echo back string from it\n",progname);
	printf("\nMandatory arguments to long options are mandatory for short options too:\n");
	printf(" -b[daemon ] set program running on background\n");
	printf(" -p[port ] Socket server port address\n");
	printf(" -h[help ] Display this help information\n");
	printf("\nExample: %s -b -p 8900\n", progname);
	return ;
}
void sign_handle(int signum)
{
    p_stop = 1;
    log_info("The program exit now!");
}

void set_socket_rlimit(void);
void daemon_switch(int daemon_ctl);
void signal_init();
int epoll_init(int *epollfd);
int epoll_wait_server();
int epoll_accept(int i);
/*---------------------------主函数----------------------------------*/
int main(int argc, char **argv)
{

	int							rv = -1;
	int							port=0;
	char						buf[128];
	int							opt;
	int							idx;
	char						*servip=NULL;
	char						*proname = NULL;
	int							daemon_val=0;
	int							events=0;
	int							i=0;
	char						sql[128];
	int							db_id=1;
	char						*mark="/";
	struct option				opts[] = {
			{"daemon", no_argument,       NULL, 'd'},
			{"port",   required_argument, NULL, 'p'},
			{"ip",     required_argument, NULL, 'i'},
			{"help",   no_argument,       NULL, 'h'},
			{NULL,     0,                 NULL,  0 }
		};


	proname = basename(argv[0]);

	while((opt = getopt_long(argc,argv,"p:h",opts,&idx)) != -1)
	{
		switch(opt)
		{
			case 'd':
				daemon_val = 1;
				break;
			case 'p':
				port =atoi(optarg);
				break;
			case 'h':
				useage(proname);
				return EXIT_SUCCESS;
			case 'i':
				strncpy(servip,optarg,sizeof(optarg));
				break;
			default:
				break;
		}
	}

	if(!port)
	{
		useage(proname);
		return -1;
	}

	signal_init();

    //是否进入守护进程，后台运行
    daemon_switch(daemon_val);

	//设置日志文件信息，初始化
    if( logger_init("running_server.log", LOG_LEVEL_INFO) < 0)
    {
        fprintf(stderr, "initial logger system failure\n");
        return -1;
    }


	//解锁epoll连接上限
	set_socket_rlimit();

	//初始化socket
	sockfd=socket_start_server(port,servip);
	
	//初始化epoll
	epoll_init(&epollfd);

	divi_info=malloc(sizeof(info_temp_t));

	while(!p_stop)
	{
		events=epoll_wait_server();
		if(events==-1)
			  break;
		if(events==-2)
			  continue;
			  
		for(i=0;i<events;i++)
		{
			if ((epoll_array[i].events&EPOLLERR) || (epoll_array[i].events&EPOLLHUP))
			{
				log_error("epoll_wait get error on fd[%d]: %s\n", epoll_array[i].data.fd, strerror(errno));
				epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_array[i].data.fd, NULL);
				close(epoll_array[i].data.fd);
			}
			if(epoll_array[i].data.fd==sockfd)
			{
				rv=epoll_accept(i);
				if(rv==-1)
					break;
				if(rv==-2)
					continue;
				sql_create_db_tb("server_temp.db");

			}
			else
			{
				memset(buf,0,sizeof(buf));
				rv = socket_read_divideinfo(&epoll_array[i].data.fd,buf,divi_info);
				if(rv <= 0)
				{
					epoll_ctl(epollfd,EPOLL_CTL_DEL,epoll_array[i].data.fd,NULL);
					close(epoll_array[i].data.fd);
					continue;
				}
				else 
				{
					log_info("read %dbyte from client[%d]:%s\n",strlen(buf),ntohs(cliaddr.sin_port),buf);
					sql_insert_one_data(divi_info);
				}
			}
		}
			
    }
	free(divi_info);
	socket_term(&sockfd);
	logger_term();
}

void set_socket_rlimit(void)
{
	struct rlimit limit = {0};
	getrlimit(RLIMIT_NOFILE, &limit );
	limit.rlim_cur = limit.rlim_max;
	setrlimit(RLIMIT_NOFILE, &limit );
	log_info("set socket open fd max count to %d\n", limit.rlim_max);
}
void daemon_switch(int daemon_ctl)
{
    if(daemon_ctl)
    {
        if(daemon(1,1)<0)
        {
            log_error("Program daemon() failure: %s\n", strerror(errno));
            return ;
        }
        else
        {
            log_info("program daemon\n");
        }
    }

}

int epoll_init(int *epollfd)
{
	if((*epollfd = epoll_create(MAX_EVENTS))<0)
	{
		log_error("epoll_create() failure: %s\n", strerror(errno));
		return -1;
	}

	epoll.events = EPOLLIN;
	epoll.data.fd = sockfd;
	if(epoll_ctl(*epollfd,EPOLL_CTL_ADD,sockfd,&epoll)<0)
	{
		log_error("epoll_ctl() add sockfd failure:%s\n",strerror(errno));
		return -2;
	}
	return 1;
}
void signal_init()
{
    struct sigaction    sigact;

    sigact.sa_handler = sign_handle;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;


    //插入kill,CTRL+C,SIGPIPE处理信号
    sigaction(SIGTERM,&sigact,0);
    sigaction(SIGINT,&sigact,0);

}

int epoll_wait_server()
{
    int events=0;
    events = epoll_wait(epollfd,epoll_array,MAX_EVENTS,-1);
	if(events < 0)
	{
		log_error("epoll_wait() failure:%s\n",strerror(errno));
		return -1;
	}
	else if(events==0)
	{
		log_error("epoll_wait get time out:%s\n",strerror(errno));
		return -2;
	}
   return events;
}

int epoll_accept(int i)
{
	clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
	if(clifd<0)
	{
		log_error("Accept new client faliure:%s\n",strerror(errno));
		return -1;
	}

	epoll.data.fd = clifd;
	epoll.events = EPOLLIN;
	if(epoll_ctl(epollfd,EPOLL_CTL_ADD,clifd,&epoll)<0)
	{
		log_error("epoll_add new clifd failure:%s\n",strerror(errno));
		close(epoll_array[i].data.fd);
		return -2;
	}

	log_info("Accept and epoll_add new client[%s:%d] successfully!\n",
		inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
}
